/* 
 * Copyright (c) 2009, Giora Kosoi giorak@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Giora Kosoi ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Giora Kosoi BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <assert.h>

#include <sparrow/variant_headers.h>

#include "test_helpers.h"

void test()
{
    VariantSet v;
    v.set(0, new VariantInt32(37));
    assert(v.get(0)->int32() == 37);
    v.get(0)->int32(38);
    assert(v.get(0)->int32() == 38);
    v.remove(0);
    assert_throws(std::runtime_error, v.get(0));
}

void test2()
{
    VariantSet v;
    VariantSet v2;
    VariantSet v3;

    for(int i = 0; i < 30; ++i)
    {
        v.insert(new VariantInt32(i));
    }

    for(int i = 0; i < 30; ++i)
    {
        v2.insert(i, new VariantInt32(i));
    }

    for(int i = 0; i < 30; ++i)
    {
        v3.set(i, new VariantInt32(i));
    }

    assert(v.array_size() == 30);
    assert(v2.array_size() == 30);
    assert(v3.array_size() == 30);

    for(int i = 0; i < 30; ++i)
    {
        assert(v.get(i)->int32() == i);
        assert(v2.get(i)->int32() == i);
        assert(v3.get(i)->int32() == i);
    }

    v.clear();
    v2.clear();
    v3.clear();

    assert_throws(std::runtime_error, v.get(0));
    assert_throws(std::runtime_error, v2.get(0));
    assert_throws(std::runtime_error, v3.get(0));
}

void test3()
{
    VariantSet v;
    assert_throws(std::runtime_error, v.get("key"));
    v.set("key", new VariantInt32(1));
    assert(v.get("key")->int32() == 1);
    v.remove("key");
    assert_throws(std::runtime_error, v.get("key"));
}

void test4()
{
    VariantSet v;
    v.set(0, new VariantInt32(1));
    v.set("key", new VariantInt32(2));
    v.set(IntKey(0), new VariantInt32(3));

    VariantSet *cloned = v.clone()->variant_set();
    assert(v.get(0)->int32() == cloned->get(0)->int32());
    assert(v.get("key")->int32() == cloned->get("key")->int32());
    assert(v.get(IntKey(0))->int32() == cloned->get(IntKey(0))->int32());
    delete cloned;
}

void test5()
{
    Variant *v = new VariantInt32(5);
    assert_throws(std::logic_error, v->variant_set());
    delete v;
}

void test6()
{
    VariantSet v;
    assert_throws(std::runtime_error, v.get(IntKey(5)));
    v.set(IntKey(5), new VariantInt32(1));
    assert(v.get(IntKey(5))->int32() == 1);
    v.remove(IntKey(5));
    assert_throws(std::runtime_error, v.get(IntKey(5)));
}

void test7()
{
    // VariantInt32
    VariantInt32 x(2);
    assert(x.int32() == 2);
    assert(x.uint32() == 2);
    assert(x.str() == "2");
    Octets o;
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(2);
    assert(x.octets() == o);

    VariantInt32 y("-2");
    assert(y.int32() == -2);
    assert(y.uint32() == -2);
    assert(y.str() == "-2");
    o = Octets();
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xfe);
    assert(y.octets() == o);

    VariantInt32 z(o);
    assert(z.int32() == -2);
    assert(z.uint32() == -2);
    assert(z.str() == "-2");
    
    z.int32(-3);
    assert(z.uint32() == -3);
    assert(z.str() == "-3");
    o = Octets();
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xfd);
    assert(z.octets() == o);

    assert_throws(std::runtime_error, z.octets((const uint8_t *)"\0\1\2\3\4", 5));
    assert_throws(std::runtime_error, z.octets((const uint8_t *)"\0\1\2\3\4", 3));
    assert_throws(std::runtime_error, z.str("abcd"));
}

void test8()
{
    // VariantInt64
    VariantInt64 x(2);
    assert(x.int64() == 2);
    assert(x.uint64() == 2);
    assert(x.str() == "2");
    Octets o;
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(0);
    o.push_back(2);
    assert(x.octets() == o);

    VariantInt64 y("-2");
    assert(y.int64() == -2);
    assert(y.uint64() == -2);
    assert(y.str() == "-2");
    o = Octets();
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xfe);
    assert(y.octets() == o);

    VariantInt64 z(o);
    assert(z.int64() == -2);
    assert(z.uint64() == -2);
    assert(z.str() == "-2");
    
    z.int64(-3);
    assert(z.uint64() == -3);
    assert(z.str() == "-3");
    o = Octets();
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xff);
    o.push_back(0xfd);
    assert(z.octets() == o);

    assert_throws(std::runtime_error, z.octets((const uint8_t *)"\0\1\2\3\4\5\6\7\0", 9));
    assert_throws(std::runtime_error, z.octets((const uint8_t *)"\0\1\2\3\4", 3));
    assert_throws(std::runtime_error, z.str("abcd"));
}

void test9()
{
    // VariantIpV4
    VariantIpV4 x("1.2.3.4");
    assert(x.str() == "1.2.3.4");
    Octets o;
    o.push_back(1);
    o.push_back(2);
    o.push_back(3);
    o.push_back(4);
    assert(x.octets() == o);
    assert(memcmp(&x.ipv4(), &o.at(0), o.size()) == 0);

    assert_throws(std::runtime_error, x.octets((const uint8_t *)"\0\1\2\3\4", 5));
    assert_throws(std::runtime_error, x.octets((const uint8_t *)"\0\1\2\3\4", 3));
    assert_throws(std::runtime_error, x.str("abcd"));
}

void test10()
{
    assert(VariantSet::vartype_to_str(Variant::VT_NULL) == "VT_NULL");
    assert(VariantSet::vartype_to_str(Variant::VT_INT32) == "VT_INT32");
    assert(VariantSet::vartype_to_str(Variant::VT_INT64) == "VT_INT64");
    assert(VariantSet::vartype_to_str(Variant::VT_IPV4) == "VT_IPV4");
    assert(VariantSet::vartype_to_str(Variant::VT_VARIANT_SET) == "VT_VARIANT_SET");
    assert(VariantSet::vartype_to_str(Variant::VT_PTR) == "VT_PTR");
    assert(VariantSet::vartype_to_str(Variant::VT_STR) == "VT_STR");
    assert(VariantSet::vartype_to_str((Variant::VariantType)2000) == "unknown_type");
}

void test11()
{
    VariantSet empty;
    assert(empty.pstr() == "[]");
    VariantSet v;
    v.insert(new VariantInt32(5));
    v.insert(new VariantInt32(7));
    v.set(IntKey(1), new VariantString("a b c"));
    v.set("i am the key", new VariantString("a b c"));
    v.set("copy", v.clone());
    assert(v.pstr() == "[5, 7; 1 : \"a b c\", \"copy\" : [5, 7; 1 : \"a b c\", \"i am the key\" : \"a b c\"], \"i am the key\" : \"a b c\"]");
}

void test12()
{
    VariantSet vs;
    assert(vs.release(42, std::nothrow) == NULL);
    assert(vs.release(IntKey(42), std::nothrow) == NULL);
    assert(vs.release("key", std::nothrow) == NULL);
    vs.set(42, new VariantString("test"));
    vs.set(IntKey(42), new VariantString("test"));
    vs.set("key", new VariantString("test"));

    Variant *v = vs.release(42, std::nothrow);
    Variant *v2 = vs.release(IntKey(42), std::nothrow);
    Variant *v3 = vs.release("key", std::nothrow);
    assert(v != NULL);
    assert(v2 != NULL);
    assert(v3 != NULL);
    delete v;
    delete v2;
    delete v3;
}

void test13()
{
    std::string s("test string");
    const char *p = s.data();
    Variant *v = new VariantString();
    v->swap(s);
    assert(s.empty());
    assert(v->str() == "test string");
    assert(p == v->str().data());
    Octets o;
    o.push_back(0);
    o.push_back(1);
    o.push_back(2);
    o.push_back(3);
    uint8_t *pp = &o.at(0);
    v->swap(o);
    assert(pp == &v->octets().at(0));
    
    delete v;
}

int main(int argc, char **argv)
{
    test();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();
    test12();
    test13();
    return 0;
}

